package org.dolphin.action.inventory;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.InterceptorRef;
import org.apache.struts2.convention.annotation.InterceptorRefs;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;
import org.dolphin.base.bean.FlatProductVO;
import org.dolphin.base.bean.ProductAttributeVO;
import org.dolphin.base.bean.ProductCategoryVO;
import org.dolphin.base.bean.ProductVO;
import org.dolphin.base.constant.Status;
import org.dolphin.base.json.PaginationBean;
import org.dolphin.base.json.StatusBean;
import org.dolphin.entity.inventory.Product;
import org.dolphin.entity.inventory.ProductAttribute;
import org.dolphin.entity.inventory.ProductCategory;
import org.dolphin.entity.inventory.ProductCategoryLink;
import org.dolphin.entity.inventory.ProductCategoryLinkPk;
import org.dolphin.service.ProductService;
import org.dolphin.service.UomService;
import org.dolphin.util.ExceptionHelper;
import org.dolphin.util.Pagination;
import org.hibernate.criterion.Order;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.opensymphony.xwork2.ActionSupport;

@ParentPackage(value = "custom-default")
@Namespace(value = "/inventory/product")
@InterceptorRefs(value = {@InterceptorRef(value = "customStack")})
public class ProductAction extends ActionSupport {
  private static final long serialVersionUID = 92362146048082021L;
  private ProductService productService;
  private UomService uomService;
  private Integer page;
  private Integer rows;
  private String sidx;
  private String sord;
  private PaginationBean pagination;
  private StatusBean status;
  private ProductVO product;

  @Action(value = "addProduct", results = @Result(name = "success", location = "addProduct.jsp"))
  public String addProduct() {
    return SUCCESS;
  }

  @Action(value = "createProduct", results = @Result(name = "success", params = {"root", "status"}, type = "json"))
  public String createProduct() {
    boolean error = false;
    String msg = "";
    status = new StatusBean();
    try {
      String name = product.getName();
      BigDecimal baseRetailPrice = product.getBaseRetailPrice();
      Assert.hasText(name, getText("error.required", new String[] {getText("field.product.name")}));
      Assert.notNull(baseRetailPrice,
          getText("error.required", new String[] {getText("field.base.retail.price")}));
      Assert
          .isTrue(
              baseRetailPrice.compareTo(BigDecimal.ZERO) >= 0,
              getText("error.min",
                  new String[] {getText("field.base.retail.price"), String.valueOf(0)}));
      Product p = new Product();
      p.setName(name);
      p.setBaseRetailPrice(baseRetailPrice);
      List<ProductCategoryVO> categoryList = product.getCategories();
      if (categoryList != null && !categoryList.isEmpty()) {
        List<ProductCategoryLink> productCategoryLinks = new ArrayList<ProductCategoryLink>(
            categoryList.size());
        for (ProductCategoryVO categoryVO : categoryList) {
          ProductCategory category = getProductService().loadProductCategory(categoryVO.getId());
          ProductCategoryLink categoryLink = new ProductCategoryLink(new ProductCategoryLinkPk(p,
              category));
          productCategoryLinks.add(categoryLink);
        }
        p.setProductCategoryLinks(productCategoryLinks);
      }
      List<ProductAttributeVO> attributes = product.getAttributes();
      if (attributes != null && !attributes.isEmpty()) {
        for (ProductAttributeVO attributeVO : attributes) {
          ProductAttribute attribute = new ProductAttribute();
          attribute.setName(attributeVO.getAttributeName());
          attribute.setStockKeepingUnit(attributeVO.getSku());
          attribute.setAdjustmentRetailPrice(BigDecimal.ZERO);
          p.addProductAttribute(attribute);
        }
      }
      getProductService().createProduct(p);
    } catch (Exception e) {
      error = true;
      msg = ExceptionHelper.getRootCause(e).getMessage();
    } finally {
      status.setStatus(error ? Status.ERROR : Status.SUCCESS);
      if (error && msg != null) {
        status.addError(msg);
      }
    }
    return SUCCESS;
  }

  @Action(value = "editProduct", results = @Result(name = "success", location = "editProduct.jsp"))
  public String editProduct() {
    return SUCCESS;
  }

  public Integer getPage() {
    return page;
  }

  public PaginationBean getPagination() {
    return pagination;
  }

  public ProductVO getProduct() {
    return product;
  }

  @Action(value = "getProducts", results = @Result(name = "success", params = {"root", "pagination"}, type = "json"))
  public String getProducts() {
    List<Order> orders = null;
    if (StringUtils.hasText(getSidx())) {
      orders = new ArrayList<Order>(1);
      Order order = ObjectUtils.nullSafeEquals("desc", getSord()) ? Order.desc(getSidx()) : Order
          .asc(getSidx());
      orders.add(order);
    }
    List<FlatProductVO> results = getProductService().getProducts(page, rows, orders);
    Long records = getProductService().getProductsCount();
    Integer total = Pagination.calculateTotalPage(records, getRows());
    pagination = new PaginationBean();
    pagination.setPage(getPage());
    pagination.setRecords(records);
    pagination.setTotal(total);
    pagination.setRows(results);
    return SUCCESS;
  }

  public ProductService getProductService() {
    return productService;
  }

  public Integer getRows() {
    return rows;
  }

  public String getSidx() {
    return sidx;
  }

  public String getSord() {
    return sord;
  }

  public StatusBean getStatus() {
    return status;
  }

  public UomService getUomService() {
    return uomService;
  }

  @Action(value = "listProduct", results = @Result(name = "success", location = "listProduct.jsp"))
  public String listProduct() {
    return SUCCESS;
  }

  @Action(value = "loadProduct", results = @Result(name = "success", params = {"root", "product"}, type = "json"))
  public String loadProduct() {
    String id = product.getId();
    product = getProductService().getProduct(id);
    return SUCCESS;
  }

  public void setPage(Integer page) {
    this.page = page;
  }

  public void setPagination(PaginationBean pagination) {
    this.pagination = pagination;
  }

  public void setProduct(ProductVO product) {
    this.product = product;
  }

  public void setProductService(ProductService productService) {
    this.productService = productService;
  }

  public void setRows(Integer rows) {
    this.rows = rows;
  }

  public void setSidx(String sidx) {
    this.sidx = sidx;
  }

  public void setSord(String sord) {
    this.sord = sord;
  }

  public void setStatus(StatusBean status) {
    this.status = status;
  }

  public void setUomService(UomService uomService) {
    this.uomService = uomService;
  }

  @Action(value = "updateProduct", results = @Result(name = "success", params = {"root", "status"}, type = "json"))
  public String updateProduct() {
    boolean error = false;
    String msg = "";
    status = new StatusBean();
    Product p = null;
    try {
      String id = product.getId();
      List<ProductCategoryVO> categories = product.getCategories();
      p = getProductService().fetchProduct(id);
      p.setVersion(product.getVersion());
      p.setName(product.getName());
      List<ProductCategoryLink> links = p.getProductCategoryLinks();
      if (categories != null && !categories.isEmpty()) {
        if (links == null) {
          links = new ArrayList<ProductCategoryLink>();
        }
        Set<String> categoryIds = new HashSet<String>(categories.size());
        for (ProductCategoryVO vo : categories) {
          categoryIds.add(vo.getId());
        }
        Set<String> existingCategoryIds = new HashSet<String>(links.size());
        if (!links.isEmpty()) {
          for (ProductCategoryLink link : links) {
            existingCategoryIds.add(link.getCategory().getId());
          }
        }

        Set<String> removedCategoryIds = new HashSet<String>(categoryIds.size());
        Set<String> newCategoryIds = new HashSet<String>(categoryIds.size());
        for (String categoryId : categoryIds) {
          boolean contains = existingCategoryIds.contains(categoryId);
          if (!contains) {
            newCategoryIds.add(categoryId);
          }
        }

        for (String categoryId : existingCategoryIds) {
          //if category id is not found in existing categories, it means, the category will be deleted
          boolean contains = categoryIds.contains(categoryId);
          if (!contains) {
            removedCategoryIds.add(categoryId);
          }
        }

        for (String removedCategoryId : removedCategoryIds) {
          for (int i = 0; i < links.size(); i++) {
            ProductCategoryLink link = links.get(i);
            ProductCategory category = link.getCategory();
            if (ObjectUtils.nullSafeEquals(category.getId(), removedCategoryId)) {
              links.remove(i);
              break;
            }
          }
        }
        for (String newCategoryId : newCategoryIds) {
          ProductCategory category = getProductService().fetchProductCategory(newCategoryId);
          ProductCategoryLink link = new ProductCategoryLink(new ProductCategoryLinkPk(p, category));
          links.add(link);
        }
      } else {
        if (links != null) {
          links.clear();
        }
      }
      p.setProductCategoryLinks(links);
      getProductService().updateProduct(p);
    } catch (Exception e) {
      error = true;
      msg = ExceptionHelper.getRootCause(e).getMessage();
    } finally {
      status.setStatus(error ? Status.ERROR : Status.SUCCESS);
      if (error && msg != null) {
        status.addError(msg);
      } else {
        if (p != null) {
          status.addAttr("version", p.getVersion());
        }
      }
    }
    return SUCCESS;
  }

  @Action(value = "viewProduct", results = @Result(name = "success", location = "viewProduct.jsp"))
  public String viewProduct() {
    return SUCCESS;
  }
}
